﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
//using System.Math;

namespace Network
{
    class Apprentissage
    {
        Network[] population;// = new Network[IndividualNumber];
        int line_number;
        int j;
        Network old_elite = new Network(3);

        public Apprentissage(int nb, int indi, int nbGen)
        {
            population = new Network[indi];
            string contentFile = Utilitaire.PickUpFileLearning();
            string[] lines = Utilitaire.ReadFileLearning(contentFile);

            this.old_elite.setError(1000.0);
            for (j = 0; j < indi; j++)
            {
                this.population[j] = new Network(nb);
            }


            double[] output = new double[nb];
            double FinalOutput;

            /* generation */
            for (int j = 0; j < nbGen; j++)
            {
                System.Console.WriteLine("génération: " + j);

                /*  network */
                for (int i = 0; i < indi; i++)
                {
                    this.population[i].setError(0);
                    /* Line */
                    foreach (string line in lines)
                    {
                        String[] input = line.Split(',');

                        /* hidden neural */
                        for (int n = 0; n < nb; n++)
                        {
                            this.population[i].getHiddenNeuronal()[n].setInputValue(int.Parse(input[0]), int.Parse(input[1]), int.Parse(input[2]));
                            output[n] = this.population[i].getHiddenNeuronal()[n].getOutput();
                        }
                        this.population[i].getFinalNeuronal().setValueInput(output);

                        FinalOutput = this.population[i].getFinalNeuronal().getOutput();
                        double err = Math.Abs((double.Parse(input[3]) - 1.0) - FinalOutput);
                        this.population[i].errorPlus(err);
                    }

                }

                this.population = Utilitaire.Sort(population);
                System.Console.WriteLine(this.population[0].getError());

                /* Save the best one */
                if (this.old_elite.getError() > this.population[0].getError())
                {
                    this.old_elite = this.population[0];
                }

            //    this.population = Biaise(population);
                this.population = Mutation(population, nb, indi);
                //System.Console.WriteLine(this.population[0].getError());
            }
            Program.bestNetwork = this.population[0];
        }


        /* elistism to keep the goods one*/
        public Network[] Elitisme(Network[] r1, int nb, int indi)
        {

            Network[] r_new = new Network[indi];
            r_new[0] = r1[0];

            /* for (int i = 0; i < indi/2 ; i++)
             {
                 r_new[i] = r1[i];
             }
             */
            for (int j = 1; j < indi; j++)
            {
                r_new[j] = new Network(nb);
            }

            return r_new;
        }

        public Network[] Biaise(Network[] r)
        {
            /*int nbTemp = 0;
            for (int t=0;t<r.Length;t++)
            {
                nbTemp += t;
            }


            Network[] temp = new Network[nbTemp];

            int decr = 11;
            for (int c = 0 ;c < Program.IndividualNumber;c++)
            {
                decr--;
               for (int a=0;a<decr;a++)
               {
                   temp[a] = r[c];
               }

            }*/
            var Roue = new List<Network>();
            for (int i = 0; i < r.Length; i++)
            {
                for (int j = 0; j < Convert.ToInt32(290 - (r[i].getError() / 10)); j++)
                {
                    Roue.Add(r[i]);
                }
            }
            for (int i = 0; i < r.Length; i++)
            {

                int temps = Convert.ToInt32(Utilitaire.GetRandomNumber(0.0, Convert.ToDouble(Roue.Count)));
                r[i] = Roue[temps];
                Roue.Remove(Roue[temps]);

            }
            return r;
        }

        public Network[] Mutation(Network[] r, int nb, int indi)
        {



            Random rand = new Random();
            for (int a = 0; a < indi; a++)
            {
                if (rand.Next(1, 100) <= 60)
                {
                    int neurone = rand.Next(1, nb);

                    int op = rand.Next(1, 2);
                    double newval;


                    switch (rand.Next(1, 5))
                    {
                        case 1:

                            newval = r[a].getHiddenNeuronal()[neurone - 1].getWeigth(0);

                            if (op == 1)
                            {
                                newval += 0.01;
                            }
                            else
                            {
                                newval -= 0.01;

                            }
                            r[a].getHiddenNeuronal()[neurone - 1].setWeight(0, newval);
                            break;
                        case 2:
                            newval = r[a].getHiddenNeuronal()[neurone - 1].getWeigth(1);

                            if (op == 1)
                            {
                                newval += 0.01;
                            }
                            else
                            {
                                newval -= 0.01;

                            }
                            r[a].getHiddenNeuronal()[neurone - 1].setWeight(1, newval);
                            break;
                        case 3:
                            newval = r[a].getHiddenNeuronal()[neurone - 1].getWeigth(2);

                            if (op == 1)
                            {
                                newval += 0.01;
                            }
                            else
                            {
                                newval -= 0.01;

                            }
                            r[a].getHiddenNeuronal()[neurone - 1].setWeight(2, newval);
                            break;
                        case 4:
                            newval = r[a].getHiddenNeuronal()[neurone - 1].getWeigth(3);

                            if (op == 1)
                            {
                                newval += 0.01;
                            }
                            else
                            {
                                newval -= 0.01;

                            }
                            r[a].getHiddenNeuronal()[neurone - 1].setWeight(3, newval);
                            break;

                        case 5:
                            newval = r[a].getFinalNeuronal().getWeigth(0);

                            if (op == 1)
                            {
                                newval += 0.01;
                            }
                            else
                            {
                                newval -= 0.01;

                            }
                            r[a].getFinalNeuronal().setWeight(0, newval);
                            break;
                        /*case 6:
                            r[a].getFinalNeuronal().setWeight(1, rand.NextDouble());
                            break;*/
                        //}
                    }
                    Thread.Sleep(25);
                }

                return r;
            }
        }
    }
}
